package zhengqc.graph.data_structure;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;

/* 有权图 */
public class WeightedGraph implements Graph {
    private int V;
    private int E;
    private TreeMap<Integer, Integer>[] adjacency;
    private boolean directed;
    private int[] inDegrees, outDegrees;

    public WeightedGraph(String filename, boolean directed) {
        this.directed = directed;
        File file = new File(filename);
        try (Scanner scanner = new Scanner(file)) {
            V = scanner.nextInt();
            if (V < 0) {
                throw new IllegalArgumentException("V must be non-negative");
            }
            E = scanner.nextInt();
            if (E < 0) {
                throw new IllegalArgumentException("E must be non-negative");
            }
            adjacency = new TreeMap[V];
            for (int i = 0; i < V; ++i) {
                adjacency[i] = new TreeMap<>();
            }

            inDegrees = new int[V];
            outDegrees = new int[V];

            for (int i = 0; i < E; ++i) {
                int a = scanner.nextInt();
                validateVertex(a);
                int b = scanner.nextInt();
                validateVertex(b);
                int weighted = scanner.nextInt();
                if (a == b) { // 自环边
                    throw new IllegalArgumentException("Self Loop is Detected!");
                }
                if (adjacency[a].containsKey(b)) { // 平行边
                    throw new IllegalArgumentException("Parallel Edges are Detected!");
                }
                adjacency[a].put(b, weighted);
                if (directed) {
                    outDegrees[a] += 1;
                    inDegrees[b] += 1;
                }
                if (!directed) {
                    adjacency[b].put(a, weighted);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public WeightedGraph(String filename) {
        this(filename, false);
    }
    @Override /* 验证 v 是否为合法的 Vertex */
    public void validateVertex(int v) {
        if (v < 0 || v >= this.V) {
            throw new IllegalArgumentException("vertex " + v + " is invalid!");
        }
    }
    @Override /* 是否为有向图 */
    public boolean isDirected() {
        return this.directed;
    }
    @Override /* 是否为有权图 */
    public boolean isWeighted() {
        return true;
    }
    @Override /* 获取 Vertex 数目 */
    public int getV() {
        return this.V;
    }
    @Override /* 获取 Edge 数目 */
    public int getE() {
        return this.E;
    }
    @Override /* v 与 w 之间是有 Edge */
    public boolean hasEdge(int v, int w) {
        validateVertex(v);
        validateVertex(w);
        return adjacency[v].containsKey(w);
    }
    @Override /* 获取与 v 相连的 Vertex 集合 */
    public Iterable<Integer> getAdjacency(int v) {
        validateVertex(v);
        return adjacency[v].keySet();
    }
    /* 获取 v 与 w 之间的 Weighted */
    public int getWeighted(int v, int w) {
        if (!hasEdge(v, w)) {
            throw new IllegalArgumentException(String.format("No edge %d-%d", v, w));
        }
        return adjacency[v].get(w);
    }
    @Override /* 获取 v 的 degree */
    public int getDegree(int v) {
        if (this.directed) {
            throw new IllegalArgumentException("degree only works in undirected graph.");
        }
        validateVertex(v);
        return adjacency[v].size();
    }
    @Override /* 获取 v 的 in degree */
    public int getInDegree(int v) {
        if (!this.directed) {
            throw new IllegalArgumentException("in-degree only works in Directed-Graph.");
        }
        validateVertex(v);
        return inDegrees[v];
    }
    @Override /* 获取 v 的 out degree */
    public int getOutDegree(int v) {
        if (!this.directed) {
            throw new IllegalArgumentException("out-degree only works in Directed-Graph.");
        }
        validateVertex(v);
        return outDegrees[v];
    }
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("WeightedGraph, V = %d, E = %d, directed = %b\n", V, E, directed));
        for (int i = 0; i < V; i++) {
            sb.append(String.format("%d : ", i));
            for (Map.Entry<Integer, Integer> entry: adjacency[i].entrySet()) {
                sb.append(String.format("(%d: %d) ", entry.getKey(), entry.getValue()));
            }
            sb.append("\n");
         }
        return sb.toString();
    }
}
